﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Objects;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using PagedList;
using RGoodSW.Logger.Interfaces;
using RGLM = RGoodSW.Logger.Models;
using CodeCamp.Infrastructure;
using CodeCamp.Infrastructure.Extensions;
using CodeCamp.Models.Logs;
using CodeCamp.Models.Shared;

namespace CodeCamp.Controllers
{
    //*** use PagedList - https://github.com/TroyGoode/PagedList
    //*** http://msdn.microsoft.com/en-us/magazine/hh288075.aspx (WebGrid helper)
    //*** http://www.dotnetcurry.com/ShowArticle.aspx?ID=618
    //*** http://stackoverflow.com/questions/4872192/checkboxlist-in-mvc3-0 (not working for me)

    [CustomAuthorize(Roles = CCRoles.RoleNames.BigCheeze)]
    public class LogsController : ControllerBase
    {
        private static class Tags
        {
            internal const string logCtlrSavedFilterModel = "logCtlrSavedFilterModel";
        }

        public LogsController(IDefaultValueProvider defaultValueProvider, ILoggerExtended logger)
            : base(defaultValueProvider, logger)
        {
        }

        private IQueryable<LogListEntryViewModel> GetProjectedLogEntries(CodeCampDataRepository ttdr,
            ILogSearchFiler filter, TimeSpan localTimeConversion, int headlineMaxLen)
        {
            if (filter == null)
                throw new ArgumentNullException("filter");

            List<string> selectedLogLevels = new List<string>();
            foreach (LogLevelViewModel llvm in filter.LogLevels)
            {
                if (llvm.IsChecked)
                    selectedLogLevels.Add(llvm.LogLevelName.ToUpper());
            }

            IQueryable<RGLM.LogEntry> initialQueryResult = null;
            if (selectedLogLevels.Count == 0)
            {
                var logEntries = ttdr.EFContext.LogEntries
                    //.Where( //*** filter for application name ***
                                    .Where(l1 => (filter.RemoteIP == null)
                                                    || (!filter.RemoteIPNotEq && filter.RemoteIP.Equals(l1.RemoteIP))
                                                    || (filter.RemoteIPNotEq && !filter.RemoteIP.Equals(l1.RemoteIP))
                                                    )
                                    .Where(l1a => (!filter.RemoteIPExcludeNull
                                                    || ((l1a.RemoteIP != null) && !l1a.RemoteIP.Equals("(null)", StringComparison.InvariantCultureIgnoreCase))))
                                    .Where(l2 => (filter.UserName == null)
                                                    || (!filter.UserNameNotEq && filter.UserName.Equals(l2.UserName))
                                                    || (filter.UserNameNotEq && !filter.UserName.Equals(l2.UserName)))
                                    .Where(l2a => (!filter.UserNameExcludeNull
                                                    || ((l2a.UserName != null) && !l2a.UserName.Equals("(null)", StringComparison.InvariantCultureIgnoreCase))))
                                    .OrderByDescending(l => l.LogEntryID);
                initialQueryResult = logEntries;
            }
            else if (filter.LogLevelsNot)
            {
                var logEntries = ttdr.EFContext.LogEntries
                    //.Where( //*** filter for application name ***
                                    .Where(l1 => (filter.RemoteIP == null)
                                                    || (!filter.RemoteIPNotEq && filter.RemoteIP.Equals(l1.RemoteIP))
                                                    || (filter.RemoteIPNotEq && !filter.RemoteIP.Equals(l1.RemoteIP))
                                                    )
                                    .Where(l1a => (!filter.RemoteIPExcludeNull
                                                    || ((l1a.RemoteIP != null) && !l1a.RemoteIP.Equals("(null)", StringComparison.InvariantCultureIgnoreCase))))
                                    .Where(l2 => (filter.UserName == null)
                                                    || (!filter.UserNameNotEq && filter.UserName.Equals(l2.UserName))
                                                    || (filter.UserNameNotEq && !filter.UserName.Equals(l2.UserName)))
                                    .Where(l2a => (!filter.UserNameExcludeNull
                                                    || ((l2a.UserName != null) && !l2a.UserName.Equals("(null)", StringComparison.InvariantCultureIgnoreCase))))
                                    .Where(l3 => !selectedLogLevels.Contains(l3.Level))
                                    .OrderByDescending(l => l.LogEntryID);
                initialQueryResult = logEntries;
            }
            else
            {
                var logEntries = ttdr.EFContext.LogEntries
                    //.Where( //*** filter for application name ***
                                    .Where(l1 => (filter.RemoteIP == null)
                                                    || (!filter.RemoteIPNotEq && filter.RemoteIP.Equals(l1.RemoteIP))
                                                    || (filter.RemoteIPNotEq && !filter.RemoteIP.Equals(l1.RemoteIP))
                                                    )
                                    .Where(l1a => (!filter.RemoteIPExcludeNull
                                                    || ((l1a.RemoteIP != null) && !l1a.RemoteIP.Equals("(null)", StringComparison.InvariantCultureIgnoreCase))))
                                    .Where(l2 => (filter.UserName == null)
                                                    || (!filter.UserNameNotEq && filter.UserName.Equals(l2.UserName))
                                                    || (filter.UserNameNotEq && !filter.UserName.Equals(l2.UserName)))
                                    .Where(l2a => (!filter.UserNameExcludeNull
                                                    || ((l2a.UserName != null) && !l2a.UserName.Equals("(null)", StringComparison.InvariantCultureIgnoreCase))))
                                    .Where(l3 => selectedLogLevels.Contains(l3.Level))
                                    .OrderByDescending(l => l.LogEntryID);
                initialQueryResult = logEntries;
            }
            int localTimeConversionHours = localTimeConversion.Hours;
            var projectedLogEntries = from l in initialQueryResult
                                      select new LogListEntryViewModel
                                      {
                                          LogEntryID = l.LogEntryID,
                                          HeadlineMaxLen = headlineMaxLen,
                                          Level = l.Level,
                                          LogTimeLocal = (DateTime)EntityFunctions.AddHours(l.LogTimeLocal, localTimeConversionHours),
                                          MachineName = l.MachineName ?? "",
                                          Message = l.Message,
                                          RemoteIP = l.RemoteIP ?? "",
                                          ThreadId = l.ThreadId,
                                          URL = l.URL ?? "",
                                          UserName = l.UserName ?? ""
                                      };
            return projectedLogEntries as IQueryable<LogListEntryViewModel>;
        }

        public void FillLogLevels(LogSearchViewModel model)
        {
            //IEnumerable<LogLevelViewModel> oldLevels = model.LogLevels;
            List<LogLevelViewModel> logLevels = new List<LogLevelViewModel>();

            logLevels.Add(new LogLevelViewModel
            {
                LogLevelId = (int)LogLevel.Debug,
                LogLevelName = LogLevel.Debug.ToString()
            });
            logLevels.Add(new LogLevelViewModel
            {
                LogLevelId = (int)LogLevel.Info,
                LogLevelName = LogLevel.Info.ToString()
            });
            logLevels.Add(new LogLevelViewModel
            {
                LogLevelId = (int)LogLevel.Warn,
                LogLevelName = LogLevel.Warn.ToString()
            });
            logLevels.Add(new LogLevelViewModel
            {
                LogLevelId = (int)LogLevel.Error,
                LogLevelName = LogLevel.Error.ToString()
            });
            logLevels.Add(new LogLevelViewModel
            {
                LogLevelId = (int)LogLevel.Fatal,
                LogLevelName = LogLevel.Fatal.ToString()
            });
            model.LogLevels = logLevels;
        }

        public ActionResult List(LogSearchViewModel model, string eventName)
        {
            return List(model, eventName, false, null);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [CustomAuthorize]
        public ActionResult List(LogSearchViewModel model, string eventName, string[] logLevel)
        {
            return List(model, eventName, true, logLevel);
        }

        private ActionResult List(LogSearchViewModel model, string eventName, bool postback, string[] logLevel)
        {
            SetLoggerThreadContextData();
            using (CodeCampDataRepository repository = new CodeCampDataRepository())
            {
                try
                {
                    var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
                    SetSharedViewBagProperties(ccEvent);
                    LogSearchViewModel savedModel = null;
                    LogSearchViewModel queryModel = null;

                    if (TempData.ContainsKey(Tags.logCtlrSavedFilterModel))
                    {
                        savedModel = TempData[Tags.logCtlrSavedFilterModel] as LogSearchViewModel;
                    }

                    LogViewModel viewModel = new LogViewModel();
                    SetSharedEventProperties(ccEvent, viewModel, repository);

                    const int defaultHeadlineMaxLen = 60;
                    const int defaultLogEntriesPerPage = 30;

                    if (model == null)
                    {
                        if (savedModel != null)
                        {
                            model = savedModel;
                        }
                        else
                        {
                            model = new LogSearchViewModel
                            {
                                Page = 1
                            };
                            FillLogLevels(model);
                        }
                    }

                    TimeSpan localTimeConversion = new TimeSpan(0); // LocalTimeConversion;
                    if (postback)
                    {
                        if (!string.IsNullOrWhiteSpace(model.ClearButton))
                        {
                            savedModel = null;
                            model = new LogSearchViewModel
                            {
                                Page = 1
                            };
                            FillLogLevels(model);
                            ModelState.Clear();
                        }
                        else
                        {
                            if (ModelState.IsValid)
                            {
                                //New search
                                model.Page = 1;
                                queryModel = model;
                                if (model.UTCAdjustMinutes.HasValue)
                                    localTimeConversion = new TimeSpan(0, model.UTCAdjustMinutes.Value, 0);
                            }
                            else
                            {
                                if (savedModel != null)
                                    queryModel = savedModel;
                            }
                            if (model.LogLevels.Count == 0)
                            {
                                FillLogLevels(model);
                            }
                            if (logLevel != null)
                            {
                                foreach (LogLevelViewModel llvm in model.LogLevels)
                                {
                                    llvm.IsChecked = logLevel.Contains(llvm.LogLevelName);
                                }
                            }
                        }
                    }
                    else
                    {
                        if (savedModel != null)
                        {
                            Nullable<int> page = model.Page;
                            model = savedModel;
                            model.Page = page;
                            if (model.UTCAdjustMinutes.HasValue)
                                localTimeConversion = new TimeSpan(0, model.UTCAdjustMinutes.Value, 0);
                        }
                        else if (model.LogLevels.Count == 0)
                        {
                            FillLogLevels(model);
                        }
                    }

                    if (queryModel == null)
                        queryModel = model;

                    if (ModelState.ContainsKey("FilterTimeTicks"))
                        ModelState.Remove("FilterTimeTicks");

                    if (!model.HeadlineMaxLen.HasValue)
                        model.HeadlineMaxLen = defaultHeadlineMaxLen;

                    viewModel.LogSearchResults = model;

                    if (postback || (savedModel != null))
                    {
#if DEBUG
                        if (!string.IsNullOrWhiteSpace(this.Request.UserHostAddress)
                            && (Request.ServerVariables["LOCAL_ADDR"] != null))
                        {
                            string serverAddr = Request.ServerVariables["LOCAL_ADDR"] as string;
                            if (!string.IsNullOrWhiteSpace(serverAddr) && serverAddr.Equals(Request.UserHostAddress))
                            {
                                localTimeConversion = new TimeSpan(0);
                            }
                        }
#endif

                        var pageIndex = model.Page ?? 1;
                        Logger.DebugFormat("Log-List LocalTimeConversion.Hours={0}", localTimeConversion.Hours);
                        var projectedLogEntries = GetProjectedLogEntries(repository, queryModel, localTimeConversion,
                            model.HeadlineMaxLen ?? defaultHeadlineMaxLen);
                        viewModel.LogSearchResults.SearchResults = projectedLogEntries.ToPagedList<LogListEntryViewModel>(pageIndex, defaultLogEntriesPerPage);
                    }
                    model.FilterTime = DateTime.Now;
                    if (TempData.ContainsKey(Tags.logCtlrSavedFilterModel))
                    {
                        TempData[Tags.logCtlrSavedFilterModel] = model;
                    }
                    else
                    {
                        TempData.Add(Tags.logCtlrSavedFilterModel, model);
                    }
                    return View(viewModel);
                }
                catch (Exception ex)
                {
                    LogException("Logs/List", ex);
                    return View("Error2");
                }
            }
        }

        public ActionResult Detail(string eventName, int id)
        {
            SetLoggerThreadContextData();
            using (CodeCampDataRepository repository = new CodeCampDataRepository())
            {
                try
                {
                    var ccEvent = EventsHelper.GetEvent(repository, eventName, DefaultEventShortName, this.User);
                    SetSharedViewBagProperties(ccEvent);
                    LogDetailViewModel viewModel = new LogDetailViewModel();
                    SetSharedEventProperties(ccEvent, viewModel, repository);
                    var logEntry = repository.EFContext.LogEntries
                        .Where(l => l.LogEntryID == id)
                        .FirstOrDefault();

                    if (logEntry == null)
                        return RedirectToAction("List");

                    viewModel.LogEntry = logEntry;

                    return View(viewModel);
                }
                catch (Exception ex)
                {
                    LogException("Logs/Detail", ex);
                    return View("Error2");
                }
            }
        }

    }
}
